/*
 * Created by SharpDevelop.
 */
using System;
using System.Collections.Generic;

namespace MiniMIPS
{
	/// <summary>
	/// Description of Simulator.
	/// </summary>
	
	public enum Status {Ready, OutOfBounds, Done};
	
	public class Simulator
	{
		public int PC;
		public List<Register> Registers;
		
		public int LEDs
		{
			get
			{
				RegisterIO r = (RegisterIO)Registers[7];
				return r.SetValue;
			}
			set
			{
				RegisterIO r = (RegisterIO)Registers[7];
				r.SetValue = value;
			}
		}
		public int SWs
		{
			get
			{
				RegisterIO r = (RegisterIO)Registers[7];
				return r.GetValue;
			}
			set
			{
				RegisterIO r = (RegisterIO)Registers[7];
				r.GetValue = value;
			}
		}
		
		
		public Dictionary<int, int> Memory;
		
		private List<Instruction> Instructions;
		
		public Simulator(List<Instruction> instructs)
		{
			Instructions = instructs;
			Registers = new List<Register>();
			Memory = new Dictionary<int, int>();
			
			for(int i = 0; i < 7; i++)
			{
				Registers.Add(new Register());
			}
			
			Registers.Add(new RegisterIO());
						
			PC = 0;
			LEDs = 0;
			SWs = 0;
		}
		
		private void Process(Instruction ins)
		{
			this.PC = PC + 1;
			
			if(ins.Op.Type == OpType.RType)
			{
				Registers[ins.Rd].Value = Func(Registers[ins.Rs].Value,
				                        	   Registers[ins.Rt].Value,
				                         	   ins.Op.Func);
			}
			else if(ins.Op.Type == OpType.IType)
			{
				int result = Func(Registers[ins.Rs].Value,
				                  ins.Imm, ins.Op.Func);
				int addr = Int8.Unsigned(result);
				
				if(ins.Op.Func == OpFunc.Add)
				{
					Registers[ins.Rt].Value = result;
				}
				else if(ins.Op.Func == OpFunc.Load)
				{
					if(Memory.ContainsKey(addr))
					{
						Registers[ins.Rt].Value = Memory[addr];
					}
					else
					{	// read from undefined memory, assumed 0
						Registers[ins.Rt].Value = 0;
					}
				}
				else if(ins.Op.Func == OpFunc.Store)
				{
					if(Memory.ContainsKey(addr))
					{
						Memory[addr] = Registers[ins.Rt].Value;
					}
					else
					{
						Memory.Add(addr, Registers[ins.Rt].Value);
					}
				}
				else if(ins.Op.Func == OpFunc.BranchEq)
				{
					if(Registers[ins.Rt].Value == Registers[ins.Rs].Value)
					{
						this.PC = PC + ins.Imm; 
					}
				}
			}
			else if(ins.Op.Type == OpType.JType)
			{
				this.PC = ins.Addr;
			}
		}
		
		private int Func(int a, int b, OpFunc func)
		{
			int ret;
			
			//System.Diagnostics.Trace.WriteLine("Func = " + a.ToString() + " " + OpFunc.GetName(typeof(OpFunc), func) + " " + b.ToString());
			
			switch(func)
			{
				case OpFunc.Add:
				case OpFunc.Load:
				case OpFunc.Store:
					ret = a + b;
					break;
				case OpFunc.And:
					ret = a & b;
					break;
				case OpFunc.Sub:
					ret = a - b;
					break;
				case OpFunc.BranchEq:
					ret = 0;
					break;
				case OpFunc.Or:
					ret = a | b;
					break;
				default:
					ret = 0;
					throw new Exception("Unrecognised OpFunc");
			}
			
			return ret;
		}
		
		
		public Status Step()
		{
			Status status;
			
			if(PC >= 0 && PC < Instructions.Count)
			{
				Process(Instructions[PC]);
				
				status = Status.Ready;
			}
			else if(PC == Instructions.Count)
			{
				status = Status.Done;	 
			}
			else
			{
				status = Status.OutOfBounds;
			}
			
			return status;
		}
		
		
	}
}
